package com.jason.java.demo.annotation;

import android.app.Activity;
import android.util.Log;
import android.view.View;

import androidx.fragment.app.Fragment;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.HashMap;

/**
 * @description: InjectUtils
 * @author: ChenBaoYang
 * @createDate: 2022/3/10 10:40 上午
 */
public class InjectUtils {

    public static void inject(Activity activity){
        injectView(activity);
        injectEvent(activity);
    }

    private static void injectView(Activity activity) {
        try{
            // 先拿到所有的成员变量
            Field[] fields = activity.getClass().getDeclaredFields();
            // 遍历所有的成员变量
            for (Field field: fields){
                Log.i("chenby", "field name = "+field.getName() + ",field type=" +field.getType());
                // 判断是当前的注解类型
                if(field.isAnnotationPresent(InjectView.class)) {
                    // 获取控件ID
                    int id = field.getAnnotation(InjectView.class).value();
                    // 通过activity的findViewById ，拿到控件对象
                    View view = activity.findViewById(id);
                    // 反射赋值给控件成员变量
                    field.set(activity, view);
                }
            }
        }catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }

    private static void injectEvent(Activity activity) {
        try{
            // 反射回去activity 的成员方法
            Method[] methods = activity.getClass().getDeclaredMethods();
            // 遍历每个成员方法，配置到对应的注解
            for (Method method: methods) {
                // 判断是当前的注解类型
                if(method.isAnnotationPresent(OnClick.class)) {
                    // 获取OnClick的注解对象
                    OnClick annotationOnClick = method.getAnnotation(OnClick.class);
                    // 获取EventType的注解对象
                    EventType eventType = annotationOnClick.annotationType().getAnnotation(EventType.class);
                    // 事件监听接口类
                    Class listenerClz = eventType.listenerType();
                    // 设置事件的方法名称
                    String listenerSetter = eventType.listenerSetter();
                    // 监听的方法名称-onClick
                    String methodName = eventType.methodName();

                    // 动态代理设置activity
                    ClickProxyHandler handler = new ClickProxyHandler(activity);
                    // 动态代理获得listener对象
                    Object listener = Proxy.newProxyInstance(listenerClz.getClassLoader(), new Class[]{listenerClz}, handler);

                    // 将onclick的方法名称名称作为key，activity被注解的click方法作为value，
                    // 存入map中，然后在动态代理的时候，根据method的名字，替换调用
                    handler.mapMethod(methodName, method);

                    // 获取注解上的ID数组
                    int[] ids = annotationOnClick.value();
                    for (int i = 0; i < ids.length; i++) {
                        // 获取view 对象
                        View view = activity.findViewById(ids[i]);
                        // 先反射拿到view的class
                        Class viewClz = view.getClass();
                        // 获取setOnClickListener的方法
                        Method listenerSetterMethod = viewClz.getMethod(listenerSetter, listenerClz);
                        if(!listenerSetterMethod.isAccessible()) {
                            listenerSetterMethod.setAccessible(true);
                        }
                        // 调用setOnClickListener
                        listenerSetterMethod.invoke(view, listener);
                    }
                }
            }

        }catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void inject(Fragment fragment){

    }

    public static void inject(View view){

    }

    static class ClickProxyHandler implements InvocationHandler{

        private Object target;

        private HashMap<String, Method> mMethodHashMap;

        public ClickProxyHandler(Object target) {
            this.target = target;
            mMethodHashMap = new HashMap<>();
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            Log.i("chenby","invoke onclick before");

            String name = method.getName();
            //将onClick方法的调用映射到activity 中的injectOnClick()方法
            Method realMethod = mMethodHashMap.get(name);
            if (null != realMethod){
                return realMethod.invoke(target, args);
            }
            Log.i("chenby","invoke onclick after");
            return null;
        }

        public void mapMethod(String name, Method annotationMethod) {
            mMethodHashMap.put(name, annotationMethod);
        }
    }

}
